package cc.eddic.pratice.programmingparadigms.nginxlog.impl;

import cc.eddic.pratice.programmingparadigms.nginxlog.LogItem;
import cc.eddic.pratice.programmingparadigms.nginxlog.LogReport;
import cc.eddic.pratice.programmingparadigms.nginxlog.NginxLogItem;
import cc.eddic.pratice.programmingparadigms.nginxlog.PageViewsAndUniqueVisitors;

import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

public class LogReportZyzImpl implements LogReport {

    static class UniqueVisitor {
        public String ip;
        public String userAgent;

        public UniqueVisitor(String ip, String userAgent) {
            this.ip = ip;
            this.userAgent = userAgent;
        }

        @Override
        public boolean equals(Object o) {
            if (o == null) {
                return false;
            }

            if (o instanceof UniqueVisitor) {
                UniqueVisitor uv = (UniqueVisitor) o;
                return Objects.equals(ip, uv.ip) &&
                        Objects.equals(userAgent, uv.userAgent);
            } else {
                return false;
            }
        }

        @Override
        public int hashCode() {
            return Objects.hash(ip, userAgent);
        }

    }


    @Override
    public PageViewsAndUniqueVisitors pageViewsAndUniqueVisitors(List<NginxLogItem> items) {
        long countPageViews =  items.size();
        long countUniqueVisitors = items.stream().map(x -> new UniqueVisitor(x.getIp(), x.getUserAgent())).
                distinct().count();

        return new PageViewsAndUniqueVisitors(countPageViews, countUniqueVisitors);

    }


    @Override
    public Map<Integer, PageViewsAndUniqueVisitors> pageViewsAndUniqueVisitorsByHour(List<NginxLogItem> items) {

        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MMM/yyyy:HH:mm:ss Z", Locale.ENGLISH);
        return items.stream()
                .collect(Collectors.groupingBy(
                        x -> {
                            LocalDateTime localDateTime = LocalDateTime.parse(x.getTime(), formatter);
                            LocalTime localTime = localDateTime.toLocalTime();
                            return localTime.getHour();
                        },
                        Collectors.collectingAndThen(
                                Collectors.toList(),
                                this::pageViewsAndUniqueVisitors
                        )
                ));

    }

    @Override
    public List<PathModel> pageViewsAndUniqueVisitorsByPath(List<LogItem> items) {
        return items.stream().collect(Collectors.groupingBy(
                LogItem::getPath
        )).entrySet().stream().map(
                entry -> pageViewsAndUniqueVisitors(entry.getKey(), entry.getValue())

        ).sorted(new ReversedPathModelComparator()).collect(Collectors.toList());

    }

    public PathModel pageViewsAndUniqueVisitors(String path, List<LogItem> items) {
        long pv = items.size();
        long uv = items.stream().collect(Collectors.groupingBy(
                item -> item.getIp() + item.getUserAgent()
        )).size();
        return new PathModel(path, pv, uv);
    }

    public static class ReversedPathModelComparator implements Comparator<PathModel> {
        @Override
        public int compare(PathModel a, PathModel b) {
            return -(Long.compare(a.getPv(), b.getPv()))  ;
        }
    }

}
